#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;
using Graphics;
#endregion

namespace Graphics3D
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class XNARenderer : Renderer
    {

        GraphicsDeviceManager graphics;
        ContentManager content;
        Game game;

        int screenWidth = 1000;
        int screenHeight = 850;
        float m_aspect = 100;

        public XNARenderer(Game game)
            //: base(game)
        {
            // TODO: Construct any child components here
            graphics = new GraphicsDeviceManager(game);
            content = new ContentManager(game.Services);
            //scene = new SceneController(this);

            // run at full speed
            game.IsFixedTimeStep = false;

            // set screen size
            InitScreen();

        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public void Initialize(Microsoft.Xna.Framework.GameComponent gameComponent)
        {
            // TODO: Add your initialization code here

            gameComponent.Initialize();
        }

        // screen-related init tasks
        public void InitScreen()
        {
            // back buffer
            graphics.PreferredBackBufferHeight = screenHeight;
            graphics.PreferredBackBufferWidth = screenWidth;
            graphics.PreferMultiSampling = false;
            graphics.ApplyChanges();
            m_aspect = (float)screenWidth / (float)screenHeight;

        }

        public override void BeginScene()
        {
            base.BeginScene();
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
        }

        /// render game objects
        public override void Render(List<Object3D> scene, Camera camera)
        {
            base.Render(scene,camera);
            //DrawMeshes();

            // draw all meshes
            foreach (Object3D geo in scene)
            {
                this.Draw(graphics.GraphicsDevice, camera as XNACamera, m_aspect,geo);
            }     
        
        }

        // draw this mesh to the screen
        private void Draw(GraphicsDevice device, XNACamera camera, float aspect, Object3D geometry)
        {
            XNAMesh xnaMesh = (geometry.Mesh as XNAMesh);
            XNAShader shader = geometry.BaseShader as XNAShader;

            Model model = xnaMesh.Model;

           

            // copy parent transforms
            Matrix[] transforms = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(transforms);

            // get a reference to the mesh to save some typing
            foreach (ModelMesh mesh in model.Meshes)
            {
                //ModelMesh mesh = Model.Meshes[MeshName];

                // make sure all our mesh parts are using the current effect
                foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    part.Effect = (geometry.BaseShader as XNAShader).Effect;
                }

                // rotate and transform mesh
                float x = MathHelper.ToRadians((float)geometry.Rotation.X);
                float y = MathHelper.ToRadians((float)geometry.Rotation.Y);
                float z = MathHelper.ToRadians((float)geometry.Rotation.Z);

                // matrix to translate this mesh from model
                // space to world space
                Matrix world =
                    transforms[mesh.ParentBone.Index] *
                    Matrix.CreateRotationY(y) *
                    Matrix.CreateRotationX(x) *
                    Matrix.CreateRotationZ(z) *
                    Matrix.CreateTranslation((float)geometry.Location.X, (float)geometry.Location.Y, (float)geometry.Location.Z);

                // set effect parameters
                if (shader.Effect is BasicEffect)
                {
                    // effect is a basic (or default XNA) effect
                    BasicEffect basic = (BasicEffect)shader.Effect;
                    basic.Projection = camera.ProjectionMatrix;
                    basic.View = camera.ViewMatrix;
                    basic.World = world;
                }
                else
                {
                    // effect is one of our custom effects
                    shader.Effect.Parameters["WorldTransform"]
                        .SetValue(world);
                    shader.Effect.Parameters["WorldViewProjection"]
                        .SetValue(
                            world *
                            camera.ViewMatrix *
                            camera.ProjectionMatrix);
                    shader.Effect.Parameters["CameraPosition"]
                        .SetValue(camera.Location);
                }

                // reset fill mode; otherwise basic effect will get 
                // confused whenever our custom "wireframe" effect runs
                device.RenderState.FillMode = FillMode.Solid;

                //draw the mesh using the effect options, set above
                mesh.Draw();
            }
        }

        public override void EndScene()
        {
            base.EndScene();
        }

        public int ScreenWidth
        {
            get { return this.screenWidth; }
            set { this.screenWidth = value; }
        }

        public int ScreenHeight
        {
            get { return this.screenHeight; }
            set { this.screenHeight = value; }
        }

        public GraphicsDeviceManager Device
        {
            get { return this.graphics; }
            //set { this.screenHeight = value; }
        }

        public ContentManager Content
        {
            get { return this.content; }
            //set { this.screenHeight = value; }
        }
    }
}


